/*
 * K2SComparator.cpp
 *
 *  Created on: 13-09-2013
 *      Author: Krzychu
 */
#include <sstream>

#include "K2SComparator.h"
#include "SearchResult.h"

using namespace std;

K2SComparator::K2SComparator(IAlgorithm* searchAlgorithm)
{
	this->searchAlgorithm = searchAlgorithm;
	this->sourceMetric = new CompareMetricSource();
	this->targetMetric = new CompareMetricTarget();
}

K2SComparator::~K2SComparator()
{

}

void K2SComparator::execute(string source, string target)
{
	generateSourceMetrics(source);
	generateTargetMetrics(target);
}

void K2SComparator::execute(string source, vector<string> results)
{
	for (unsigned int i = 0; i < results.size(); i++)
	{
		execute(source, results[i]);
	}
}

void K2SComparator::generateSourceMetrics(string source)
{
	splitWords(source, sourceMetric);
}

void K2SComparator::generateTargetMetrics(string source)
{
	splitWords(source, targetMetric);
}

void K2SComparator::splitWords(string str, CompareMetric* metric)
{
	const char* delimiter = " ";

	string currStr = str;

	size_t pos;
	do
	{
		pos = currStr.find(delimiter);
		metric->addWord(currStr.substr(0, pos));

		if (pos >= currStr.length() - 1)
		{
			break;
		}

		currStr = currStr.substr(pos + 1, currStr.length());
	}
	while (true);
}

bool entryExists(vector<pair<int, int> > results, int position, int length)
{
	bool result = false;

	for (int i = 0; i < results.size(); i++)
	{
		if ((position >= results[i].first && position <= results[i].first + results[i].second) ||
			(position < results[i].first && position + length >= results[i].first))
		{
			result = true;
			break;
		}
	}

	return result;
}

vector<pair<int, int> > compareNWords(map<int, vector<string> > sourceNWords, map<int, vector<string> > targetNWords, int nWordLength, int startWordIndex)
{
	vector<pair<int, int> > result;

	for (int i = 0; i < targetNWords[nWordLength].size(); i++)
	{
		string sourceWord = sourceNWords[nWordLength][startWordIndex];
		string targetWord = targetNWords[nWordLength][i];

		if (sourceWord.compare(targetWord) == 0)
		{
			vector<pair<int, int> > nextLevelResult = compareNWords(sourceNWords, targetNWords, nWordLength + 1, startWordIndex);

			if (nextLevelResult.size() == 0)
			{
				result.push_back(pair<int, int>(startWordIndex, nWordLength));
			}
			else
			{
				result.insert(result.end(), nextLevelResult.begin(), nextLevelResult.end());
			}
		}
	}

	return result;
}

vector<string> K2SComparator::findExactMatches(int minWordCount, int maxWordCount)
{
	vector<pair<int, int> > matches;

	sourceMetric->generateNWords(maxWordCount);
	targetMetric->generateNWords(maxWordCount);

	map<int, vector<string> > sourceNWords = sourceMetric->getNWords();
	map<int, vector<string> > targetNWords = targetMetric->getNWords();

	int smallestNWord = minWordCount >= 2 ? minWordCount : 2;

	for (int i = 0; i < sourceNWords[smallestNWord].size(); i++)
	{
		vector<pair<int, int> > entry = compareNWords(sourceNWords, targetNWords, smallestNWord, i);

		if (entry.size() > 0)
		{
			for (int j = 0; j < entry.size(); j++)
			{
				if (!entryExists(matches, entry[j].first, entry[j].second))
				{
					matches.push_back(entry[j]);
				}
			}
		}
	}

	vector<string> result;

	for (int i = 0; i < matches.size(); i++)
	{
		ostringstream resultString;
		for (int j = 0; j < matches[i].second; j++)
		{
			resultString << sourceMetric->getWords()[matches[i].first + j];

			if (j != matches[i].second - 1)
			{
				resultString << " ";
			}
		}

		result.push_back(resultString.str());
	}

	return result;
}

CompareMetricSource* K2SComparator::getSourceMetrics()
{
	return sourceMetric;
}

CompareMetricTarget* K2SComparator::getTargetMetrics()
{
	return targetMetric;
}
